package csx.study.demo.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import csx.study.demo.dto.Result;
import csx.study.demo.entity.Shop;
import csx.study.demo.mapper.ShopMapper;
import csx.study.demo.service.IShopService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

//用hash还是value看要不要单字段更新，要就hash不要就value，这个场景更适合value
//这种情况不用单独开一个实现类，用一个方法调用其他方法就行，高级点用策略模式
@Service
public class ShopServiceHashImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final String KEY_PREFIX = "cache:shop";
    private static final String NULL_MARKER_KEY = "__NULL__"; // 缓存key空值
    private static final String NULL_MARKER_VALUE = "1";  // value写什么都行
    private static final long CACHE_NULL_TTL = 2;    // 空值缓存
    private static final long CACHE_SHOP_TTL = 30;   // 正常缓存

    @Override
    public Result queryShopById(Long id) {
        String key = KEY_PREFIX + id;

        // 1. 先读 Hash
        Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(key);
        if (!map.isEmpty()) {
            // 1.1 如果是空占位
            if (map.containsKey(NULL_MARKER_KEY)) {
                return Result.fail("店铺不存在");
            }
            Shop shop= BeanUtil.fillBeanWithMap(map,new Shop(),false);
            return Result.ok(shop);
        }

        // 2. 缓存未命中，查库
        Shop shop = getById(id);
        if (shop == null) {
            // 2.1 DB 也不存在 -> 写空占位 Hash
            stringRedisTemplate.opsForHash().put(key, NULL_MARKER_KEY, NULL_MARKER_VALUE);
            stringRedisTemplate.expire(key, CACHE_NULL_TTL, TimeUnit.MINUTES);
            return Result.fail("店铺不存在");
        }

        // 3. DB 有数据
        //beanToMap 默认会把所有 null 字段也写进去，这样hash里会有大量无意义的字段
        //Map<String, Object> data = BeanUtil.beanToMap(shop);
        //TODO 封装一个过滤null值的方法
        Map<String, Object> data = BeanUtil.beanToMap(shop, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, value) -> value.toString()));

        stringRedisTemplate.opsForHash().putAll(key, data);
        stringRedisTemplate.expire(key, CACHE_SHOP_TTL, TimeUnit.MINUTES);
        return Result.ok(shop);
    }

    @Override
    public Result updateShop(Shop shop) {
        //重复代码
        save(shop);
        return Result.ok(shop.getId());
    }
}
